#pragma once

// -----------------------------------------------------------------------------

#include <shlwapi.h>
#include "Win32.Diagnostics.Result.h"
#include "Win32.String.h"

namespace Kif
{
namespace Win32
{
namespace IO
{

#define IsSlash(x) (*x == '/') || (*x == '\\')

/// <summary>provide support for commong file handling functions</summary>
class File
{
private:
   Kif::Win32::Diagnostics::Result lastResult;
public:

   File()
   {
      lastResult.Clear();
   }  // end of File (ctor)

   Kif::Win32::Diagnostics::Result *GetLastResult()
   {  return(&lastResult); }

   /// <summary>Given a file path return the file-name component</summary>
   /// <param name="filePath">file path to scan</param>
   /// <param name="filePathLength">file path length</param>
   /// <returns>the file name is returned</returns>
   inline static TCHAR *GetFileName(TCHAR *filePath,size_t filePathLength)
   {
      DWORD c = 0;
      TCHAR *fname = filePath,*cstr = filePath;
      while (c < filePathLength)
      {
         if (IsSlash(fname))
            cstr = fname + 1;
         fname++;
         c++;
      }
      return(cstr);
   }  // end of GetFileName
   inline static TCHAR *GetFileName(Kif::Win32::String *filePath)
   {
      if (filePath == NULL)
         return(NULL);
      return(GetFileName(filePath->GetBuffer(),filePath->GetLength()));
   }  // end of GetFileName
   inline static TCHAR *GetFileName(Kif::Win32::String &filePath)
   {
      return(GetFileName(filePath.GetBuffer(),filePath.GetLength()));
   }  // end of GetFileName

   /// <summary>Copy a source file to given destination</summary>
   /// <param name="destination">destination file full path name</param>
   /// <param name="source">source file full path name</param>
   /// <returns>if success it returns NULL, else a result with the errors
   /// is returned</returns>
   inline bool File::Copy(TCHAR *source, TCHAR *destination)
   {
      lastResult.Clear();
      if ((destination == NULL) || (source == NULL))
      {
         lastResult.ErrorNo = ERROR_INVALID_DATA;
         return(false);
      }

      BOOL done = CopyFile(source,destination,FALSE);
      if (done)
         return(true);
      lastResult.ErrorNo = GetLastError();
      return(false);
   }  // end of Copy

   inline bool Copy(Kif::Win32::String *source, Kif::Win32::String *destination)
   {
      if ((destination == NULL) || (source == NULL))
      {
         lastResult.ErrorNo = ERROR_INVALID_DATA;
         return(false);
      }
      if ((destination->GetLength() == 0) || (source->GetLength() == 0))
      {
         lastResult.ErrorNo = ERROR_INVALID_DATA;
         return(false);
      }
      return(Copy(destination->GetBuffer(),source->GetBuffer()));
   }  // end of Copy

   inline bool Copy(Kif::Win32::String &source, Kif::Win32::String &destination)
   {
      if ((destination.GetLength() == 0) || (source.GetLength() == 0))
      {
         lastResult.ErrorNo = ERROR_INVALID_DATA;
         return(false);
      }
      return(Copy(destination.GetBuffer(),source.GetBuffer()));
   }  // end of Copy

   // --------------------------------------------------------------------------

   /// <summary>return true if file exists</summary>
   /// <param name="path">file full path name</param>
   /// <returns>return true if file exists</returns>
   inline bool Exists(TCHAR *path)
   {
      lastResult.Clear();
      if (path == NULL)
      {
         lastResult.ErrorNo = ERROR_INVALID_DATA;
         return(false);
      }
      BOOL exists = PathFileExists(path);
      if (exists)
         return(true);
      lastResult.ErrorNo = GetLastError();
      return(false);
   }  // end of Exists

   inline bool Exists(Kif::Win32::String *path)
   {
      if (path == NULL)
      {
         lastResult.ErrorNo = ERROR_INVALID_DATA;
         return(false);
      }
      return(Exists(path->GetBuffer()));
   }  // end of Exists

   inline bool Exists(Kif::Win32::String &path)
   {
      return(Exists(path.GetBuffer()));
   }  // end of Exists

   // --------------------------------------------------------------------------

   /// <summary>try to delete given file</summary>
   /// <param name="path">file full path name</param>
   /// <returns>return true if was deleted</returns>
   inline bool Delete(TCHAR *path)
   {
      lastResult.Clear();
      if (path == NULL)
      {
         lastResult.ErrorNo = ERROR_INVALID_DATA;
         return(false);
      }
      BOOL done = DeleteFile(path);
      if (!done)
         lastResult.ErrorNo = GetLastError();
      return(done == TRUE);
   }  // end of Delete

   inline bool Delete(Kif::Win32::String *path)
   {
      if (path == NULL)
      {
         lastResult.ErrorNo = ERROR_INVALID_DATA;
         return(false);
      }
      return(Delete(path->GetBuffer()));
   }  // end of Delete

   inline bool Delete(Kif::Win32::String &path)
   {
      return(Delete(path.GetBuffer()));
   }  // end of Delete

   // --------------------------------------------------------------------------

   /// <summary>try to move given file</summary>
   /// <param name="path">file full path name</param>
   /// <returns>return true if was deleted</returns>
   inline bool Move(TCHAR *sourcePath, TCHAR *destinationPath)
   {
      lastResult.Clear();
      if ((sourcePath == NULL) || (destinationPath == NULL))
      {
         lastResult.ErrorNo = ERROR_INVALID_DATA;
         return(false);
      }
      BOOL done = MoveFile(sourcePath,destinationPath);
      if (!done)
         lastResult.ErrorNo = GetLastError();
      return(done == TRUE);
   }  // end of Move

   inline bool Move(
      Kif::Win32::String *sourcePath, Kif::Win32::String *destinationPath)
   {
      if ((sourcePath == NULL) || (destinationPath == NULL))
      {
         lastResult.ErrorNo = ERROR_INVALID_DATA;
         return(false);
      }
      return(Move(sourcePath->GetBuffer(),destinationPath->GetBuffer()));
   }  // end of Move

   inline bool Move(
      Kif::Win32::String &sourcePath, Kif::Win32::String &destinationPath)
   {
      return(Move(sourcePath.GetBuffer(),destinationPath.GetBuffer()));
   }  // end of Move

   // --------------------------------------------------------------------------

   /// <summary>get file size</summary>
   /// <param name="path">file full path name</param>
   /// <returns>return the size of the file, else -1</returns>
   inline DWORD FileSize(TCHAR *path)
   {
      // open file for read access
      HANDLE hFile = CreateFile(path,
         GENERIC_READ,                     // desired access
         FILE_SHARE_READ,                  // shared access
         NULL,                             // specify default security
         OPEN_EXISTING,                    // creation disposition
         FILE_ATTRIBUTE_NORMAL |
         FILE_FLAG_SEQUENTIAL_SCAN,        // flags and attributes
         NULL);                            // template file

      if (hFile == INVALID_HANDLE_VALUE)
      {
         lastResult.ErrorNo = GetLastError();
         return((DWORD)-1);
      }

      // 
      DWORD dwSizeHigh;
      DWORD dwSize = GetFileSize(hFile, &dwSizeHigh);
      lastResult.ErrorNo = GetLastError();
      if ((dwSize == 0xFFFFFFFF) && (lastResult.ErrorNo != NO_ERROR))
      {
         CloseHandle(hFile);
         return((DWORD)-1);
      }

      CloseHandle(hFile);
      return(dwSize);
   }  // end of FileSize

}  ;  // end of File class

}  // end of Kif::Win32::IO
}  // end of Kif::Win32
}  // end of Kif

